# uncompyle6 version 3.2.3
# Python bytecode 3.6 (3379)
# Decompiled from: Python 3.6.8 |Anaconda custom (64-bit)| (default, Feb 21 2019, 18:30:04) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: server\routers\upload.py
__author__ = "Attila Gerendi (Sanyi)"
import re, werkzeug.http
from werkzeug.wrappers import Request
from server.http_responses.http_errors import RouteNotFound, BadRequest
from server.http_responses.http_status_codes import HTTPFileChunkReceived
from server.routers import Router
from server.routers.api.validator import validate_value
from urllib.parse import unquote as urllib_parse_unquote
from settings_provider import settings
from helpers.rate_limiter import RateLimiter
from server.http_responses.http_errors import TooManyRequests


class UploadRequest(Request):
    max_content_length = 1050624
    max_form_memory_size = 1050624


re_content_disposition = re.compile(
    '\\s*attachment;\\s*filename="([%A-Za-z0-9\\._\\-\\(\\)\\s]*)".*'
)


class UploadRouter(Router):
    MIN_CHUNK_SIZE = 4096
    MAX_CHUNK_SIZE = 1048576

    def __init__(self, base_path, upload_handler, host=None, schemes=None):
        super().__init__(
            base_path=base_path, methods=("post",), host=host, schemes=schemes
        )
        self._len_base_path = len(base_path)
        self.upload_handler = upload_handler

    def handle_request(self, method, scheme, path, environment, start_response):
        remote_address = environment.get(
            "HTTP_X_REAL_IP",
            environment.get("HTTP_X_FORWARDED_FOR", environment["REMOTE_ADDR"]),
        )
        rt_upload_id_brute_force = settings.get("rt_upload_id_brute_force")
        if rt_upload_id_brute_force:
            r = rt_upload_id_brute_force.wait_time(remote_address)
            if r:
                raise TooManyRequests(r)
        content_type = environment.get("CONTENT_TYPE", None)
        if content_type:
            if not content_type.lower().startswith("application/octet-stream"):
                raise RouteNotFound()
        upload_id = path[self._len_base_path + 1 :]
        problems, value = validate_value(upload_id, dict(type="string", format="uuid"))
        if problems:
            raise RouteNotFound()
        request = Request(environment)
        content_range = request.headers.get("Content-Range")
        if not content_range:
            content_range = request.headers.get("X-Content-Range")
        if not content_range:
            raise BadRequest("Content-Range: not found")
        content_disposition = request.headers.get("Content-Disposition")
        if not content_disposition:
            raise BadRequest("Content-Disposition: not found")
        content_range = werkzeug.http.parse_content_range_header(content_range)
        if not content_range:
            raise BadRequest("Content-Range: invalid format")
        m = re_content_disposition.match(content_disposition)
        if not m:
            raise BadRequest(
                'Content-Disposition: invalid format (ex. attachment; filename="file.txt")'
            )
        file_filename = urllib_parse_unquote(m.group(1))
        if content_range.stop == 0 or content_range.stop <= content_range.start:
            raise BadRequest("Content-Range: is invalid")
        upload_reported_size = content_range.stop - content_range.start
        if upload_reported_size > self.MAX_CHUNK_SIZE:
            raise BadRequest(
                "Content-Range: chunk too big (max %s Bytes)" % (self.MAX_CHUNK_SIZE,)
            )
        upload_descriptor = self.upload_handler.get_descriptor(upload_id)
        if upload_descriptor is None:
            if rt_upload_id_brute_force:
                rt_upload_id_brute_force.add(remote_address)
            raise RouteNotFound()
        if file_filename != upload_descriptor["name"]:
            raise BadRequest("Incorrect file name")
        if content_range.length != upload_descriptor["size"]:
            raise BadRequest(
                "Content-Range: invalid file size, correct: %s"
                % (upload_descriptor["size"],)
            )
        if upload_reported_size < self.MIN_CHUNK_SIZE:
            if content_range.stop != upload_descriptor["size"]:
                raise BadRequest(
                    "Content-Range: chunk too small (min %s Bytes)"
                    % (self.MIN_CHUNK_SIZE,)
                )
        upload_current_size = (
            upload_descriptor["current_size"]
            if upload_descriptor["current_size"]
            else 0
        )
        if content_range.start != upload_current_size:
            raise BadRequest(
                "Content-Range: incorrect chunk start, correct:%s"
                % (upload_descriptor["current_size"],)
            )
        if content_range.stop > upload_descriptor["size"]:
            raise BadRequest(
                "Content-Range: incorrect chunk end, correct max :%s"
                % (upload_descriptor["size"] - 1,)
            )
        with open(upload_descriptor["destination"], "ab+") as (f):
            f.seek(content_range.start)
            while True:
                data = request.stream.read(8192)
                if not data:
                    break
                f.write(data)

        if content_range.stop == upload_descriptor["size"]:
            self.upload_handler.set_upload_complete(upload_id, upload_descriptor)
        else:
            self.upload_handler.add_chunk(
                upload_id, upload_descriptor["current_size"], content_range.stop
            )
        response = HTTPFileChunkReceived(
            content_range.start, content_range.stop - 1, upload_descriptor["size"]
        )
        start_response(response.response_status, response.headers)
        yield from response()
